package com.jse.api;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.jse.Http;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.Times;
import com.jse.json.JsonObject;

/**
 * 提供了与阿里云视频点播服务交互的方法。
 */
public class Media {

    // 阿里云账号 AccessKey 密钥（Access Key Secret）
    private static String access_key_secret;

    // 视频点播服务的域名
    private final static String VOD_DOMAIN = "http://vod.cn-shanghai.aliyuncs.com";

    // HTTP 请求方法，默认为 GET
    private final static String HTTP_METHOD = "GET";

    // 签名算法，使用 HMAC-SHA1
    private final static String HMAC_SHA1_ALGORITHM = "HmacSHA1";

    /**
     * 根据提供的参数生成并发送 API 请求。
     * 
     * @param param 包含 API 请求参数的 Map
     * @return 请求 URL
     */
    public static String api(Map<String, Object> param) {
        // 设置 access_key_secret 和 AccessKeyId
        access_key_secret = (String) param.getOrDefault("AccesskeySecret", Jse.conf.getString("ali.secert"));
        param.put("AccessKeyId", param.getOrDefault("AccessKeyId", Jse.conf.getString("ali.key")));

        // 生成公共参数
        Map<String, String> publicParams = generatePublicParamters();

        // 生成 OpenAPI 地址
        return generateOpenAPIURL(publicParams, new JsonObject(param).smap());
    }

    /**
     * 获取带有鉴权参数的 URL。
     * 
     * @param uri 资源 URL
     * @param key 鉴权密钥
     * @return 带鉴权参数的完整 URL
     */
    public static String url(String uri, String key) {
        String host = ""; // 存储主机部分
        if (uri.contains("?")) {
            uri = uri.substring(0, uri.indexOf("?"));
        }
        if (uri.startsWith("http")) {
            host += uri.substring(0, uri.indexOf("//") + 2);
            uri = uri.substring(uri.indexOf("//") + 2, uri.length());
        }
        host += uri.substring(0, uri.indexOf("/"));
        String url = uri.substring(uri.indexOf("/"), uri.length());
        long timestamp = System.currentTimeMillis();
        String rand = Lang.uuid();
        String mh = Lang.md5(url + "-" + timestamp + "-" + rand + "-0-" + key);
        return host + url + "?auth_key=" + timestamp + "-" + rand + "-0-" + mh;
    }

    /**
     * 生成视频点播 OpenAPI 公共参数。
     * 
     * @return 公共参数 Map
     */
    private static Map<String, String> generatePublicParamters() {
        Map<String, String> publicParams = new HashMap<>();
        publicParams.put("Format", "JSON");
        publicParams.put("Version", "2017-03-21");
        publicParams.put("SignatureMethod", "HMAC-SHA1");
        publicParams.put("Timestamp", Times.utc());
        publicParams.put("SignatureVersion", "1.0");
        publicParams.put("SignatureNonce", Lang.uuid());
        return publicParams;
    }

    /**
     * 生成 OpenAPI 地址。
     * 
     * @param publicParams 公共参数
     * @param privateParams 私有参数
     * @return 最终的 URL
     */
    private static String generateOpenAPIURL(Map<String, String> publicParams, Map<String, String> privateParams) {
        return generateURL(VOD_DOMAIN, HTTP_METHOD, publicParams, privateParams);
    }

    /**
     * 生成最终的 OpenAPI 请求 URL。
     * 
     * @param domain 请求地址
     * @param httpMethod HTTP 请求方式（GET, POST 等）
     * @param publicParams 公共参数
     * @param privateParams 接口的私有参数
     * @return 最后的 URL
     */
    private static String generateURL(String domain, String httpMethod, Map<String, String> publicParams,
            Map<String, String> privateParams) {
        List<String> allEncodeParams = getAllParams(publicParams, privateParams);
        String cqsString = getCQS(allEncodeParams);
        String stringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(cqsString);
        String signature = hmacSHA1Signature(access_key_secret, stringToSign);
        return domain + "?" + cqsString + "&" + percentEncode("Signature") + "=" + percentEncode(signature);
    }

    /**
     * 获取所有参数的编码版本。
     * 
     * @param publicParams 公共参数
     * @param privateParams 私有参数
     * @return 所有参数的编码列表
     */
    private static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
        List<String> encodeParams = new ArrayList<>();
        if (publicParams != null) {
            for (Map.Entry<String, String> entry : publicParams.entrySet()) {
                String encodeKey = percentEncode(entry.getKey());
                String encodeVal = percentEncode(entry.getValue());
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        if (privateParams != null) {
            for (Map.Entry<String, String> entry : privateParams.entrySet()) {
                String encodeKey = percentEncode(entry.getKey());
                String encodeVal = percentEncode(entry.getValue());
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        return encodeParams;
    }

    /**
     * 对参数进行 URL 编码。
     * 
     * @param value 待编码的字符串
     * @return 编码后的字符串
     */
    private static String percentEncode(String value) {
        try {
            String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
            String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
            String starReplaced = plusReplaced.replace("*", "%2A");
            String waveReplaced = starReplaced.replace("%7E", "~");
            return waveReplaced;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 获取 CQS (Canonicalized Query String) 字符串。
     * 
     * @param allParams 所有参数的列表
     * @return CQS 字符串
     */
    private static String getCQS(List<String> allParams) {
        Collections.sort(allParams, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        StringBuilder cqString = new StringBuilder();
        for (String param : allParams) {
            cqString.append(param);
            cqString.append("&");
        }
        if (cqString.length() > 0) {
            cqString.deleteCharAt(cqString.length() - 1); // 删除最后一个 &
        }
        return cqString.toString();
    }

    /**
     * 生成 HMAC-SHA1 签名。
     * 
     * @param accessKeySecret 密钥
     * @param stringtoSign 要签名的字符串
     * @return 签名字符串
     */
    private static String hmacSHA1Signature(String accessKeySecret, String stringtoSign) {
        try {
            String key = accessKeySecret + "&";
            SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signKey);
            byte[] rawHmac = mac.doFinal(stringtoSign.getBytes());
            return Base64.getEncoder().encodeToString(rawHmac);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate HMAC : " + e.getMessage());
        }
    }

    /**
     * 主方法用于测试 API 请求。
     * 
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        JsonObject map = new JsonObject();
        map.put("AccessKeyId", "你的id");
        map.put("AccesskeySecret", "你的secret");

        // 示例：获取视频列表
//        map.put("Action", "GetVideoList");
//        map.put("Status", "Normal");
//        map.put("PageNo", "1");
//        map.put("PageSize", "1");
//        map.put("SortBy", "CreationTime:Asc");

        // 示例：搜索媒体
        map.put("Action", "SearchMedia");
        map.put("ScrollToken", new java.sql.Date(System.currentTimeMillis()).toString()); // 第一次请求不需要此参数
        map.put("Fields", "Title,CoverURL,Size,Duration,Status,Tags,CateName");
        map.put("SortBy", "CreationTime:Asc"); // 可以改为 CreationTime:Desc
        map.put("PageNo", "1");
        map.put("PageSize", "1");

        // 示例：获取播放信息
//        map.put("Action", "GetPlayInfo");
//        map.put("VideoId", "2caa8830667e71ee80580764a0fd0102");
//        map.put("Definition", "FD");

        // 示例：获取 Mezzanine 信息
//        map.put("Action", "GetMezzanineInfo");
//        map.put("VideoId", "2caa8830667e71ee80580764a0fd0102");

        String url = api(map);
        System.out.println(url);
        System.out.println(Http.get(url));
    }
}